package com.ruoyi.central.publisher.subscription.material;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.central.domain.TopCentralMaterial;
import com.ruoyi.central.domain.TopCentralProduct;
import com.ruoyi.central.domain.TopCentralProductRelation;
import com.ruoyi.central.domain.TopCentralProductType;
import com.ruoyi.central.enums.MaterialStatusEnum;
import com.ruoyi.central.enums.ProductMaterialType;
import com.ruoyi.central.publisher.event.TopCentralMaterialEvent;
import com.ruoyi.central.service.ITopCentralMaterialService;
import com.ruoyi.central.service.ITopCentralProductRelationService;
import com.ruoyi.central.service.ITopCentralProductService;
import com.ruoyi.central.service.ITopCentralProductTypeService;
import com.ruoyi.common.enums.SignEnum;
import com.ruoyi.common.enums.common.State;
import com.ruoyi.common.exception.base.manipulation.UpdateDataException;
import com.ruoyi.common.exception.base.service.ServiceException;
import com.ruoyi.common.utils.CollectionUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.bean.EnumUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Classname TopCenteralMaterialEventSubscription
 * @Author: sunluquan
 * @Date: 2022/4/16 19:49
 * @Description TODO
 * 订阅物料对象发布的事件
 */
@Order(1)
@Component
public class TopCentralMaterialEventSubscription {

    @Autowired
    private ITopCentralMaterialService materialService;

    @Autowired
    private ITopCentralProductTypeService productTypeService;
    
    @Autowired
    private ITopCentralProductRelationService productRelationService;

    @Autowired
    private ITopCentralProductService productService;
    /**
     * 1.判断当前物料类型 是否为 半成品
     * 否 退出
     * 是 继续
     * 2.根据合成的物料编号 获取 合成物料集合
     * 3.获取当前半成品类型 的 子物料类型集合
     * 4.判断 2数据 和 3数据 是否一致
     * @param materialOfInsert 物料新增对象
     * @return
     */
    private boolean checkTopCentralMaterialForInsert(TopCentralMaterial materialOfInsert){

        String materialNo = materialOfInsert.getId();
        Integer materialTypeId = materialOfInsert.getTypeId();
        Map<? extends Serializable, TopCentralProductType> materialTypeMap = productTypeService.selectAllMap();
        TopCentralProductType materialType = materialTypeMap.get(materialTypeId);

        //Assert.notNull(materialType,"物料编号："+materialNo+"，绑定的物料类型不可用或不存在！");

        if(!EnumUtils.equals(ProductMaterialType.SEMI_FINISHED_PRODUCT,materialType.getProductMaterialTypeId())){
            return Boolean.FALSE;
        }
        //需要合成的物料编号集合
        Set<String> childMaterialIds = materialOfInsert.getChildMaterialIds();
        //子物料集合
        List<TopCentralMaterial> childMaterialList = null;
        
        Assert.notEmpty(childMaterialIds,"物料编号："+materialNo+"，合成物料列表不能为空！");

        childMaterialList = materialService.selectUsableByIds(childMaterialIds, TopCentralMaterial::getId, TopCentralMaterial::getMaterialStatus);
        
        Assert.notEmpty(childMaterialList,"物料编号："+materialNo+"，合成物料列表数据不可用或不存在！");

        if(childMaterialIds.size()-childMaterialList.size()!=0){
            throw new ServiceException("物料编号：{}，合成物料列表存在不可用或不存在数据！",materialNo);
        }
        Set<TopCentralMaterial> childMaterialListOfFinishedStatus = childMaterialList.stream().filter(material -> EnumUtils.equals(MaterialStatusEnum.FINISHED_OF_STATUS, material.getMaterialStatus()))
                .collect(Collectors.toSet());

        if(childMaterialList.size() - childMaterialListOfFinishedStatus.size()!=0){
            throw new ServiceException("物料编号：{}，合成物料列表的物料状态必须为：{}！",materialNo,MaterialStatusEnum.FINISHED_OF_STATUS.getDescription());
        }

        //合成所需要的物料列表
        //k为零件物料类型编号
        Map<Integer,TopCentralProductRelation> requiredMaterialListForSynthetic = productRelationService.selectAllMap().values()
                .stream().filter(productRelation -> StringUtils.equals(productRelation.getProductTypeId(), materialTypeId))
                .map(productRelation -> {
                    if(!materialTypeMap.containsKey(productRelation.getProductPartTypeId())){
                        throw new ServiceException("物料类型：{} 下合成物料类型编号：{} 不可用或不存在！",materialType.getName(),productRelation.getProductPartTypeId());
                    }
                    productRelation.setProductType(materialTypeMap.get(productRelation.getProductPartTypeId()));
                    return productRelation;
                })
                .collect(Collectors.toMap(TopCentralProductRelation::getProductPartTypeId,item->item));

        if(requiredMaterialListForSynthetic.size()-childMaterialListOfFinishedStatus.size()!=0){

            List<String> requiredMaterialTypeNameList = requiredMaterialListForSynthetic.values().stream()
                    .map(TopCentralProductRelation::getProductPartType)
                    .map(TopCentralProductType::getName).collect(Collectors.toList());
            throw new ServiceException("物料编号：{}，类型为：{} ，合成需要用到如下物料类型：{}",materialNo,materialType.getName(),Arrays.toString(requiredMaterialTypeNameList.toArray()));
        }

        Set<Integer> requiredPartMaterialIdsForSynthetic = requiredMaterialListForSynthetic.keySet();

        childMaterialList.forEach(item->requiredPartMaterialIdsForSynthetic.remove(item.getTypeId()));

        if(requiredPartMaterialIdsForSynthetic.isEmpty()){
            List<String> requiredMaterialTypeNameList = requiredMaterialListForSynthetic.values().stream()
                    .filter(productRelation -> requiredPartMaterialIdsForSynthetic.contains(productRelation.getProductPartTypeId()))
                    .map(TopCentralProductRelation::getProductPartType)
                    .map(TopCentralProductType::getName).collect(Collectors.toList());

            //获取当前新增的物料类型 下的子物料类型集合 中 不需要的类型名称列表
            List<String> unwantedMaterialTypeNameList = childMaterialList.stream().map(TopCentralMaterial::getProductTypeId)
                    //不在需要物料集合中
                    .filter(childMaterialTypeId->!requiredMaterialListForSynthetic.containsKey(childMaterialTypeId))
                    //当前物料类型编号在 物料对象集合中
                    .filter(materialTypeMap::containsKey)
                    //获取物料对象
                    .map(materialTypeMap::get)
                    //获取物料对象的名称
                    .map(TopCentralProductType::getName)
                    .collect(Collectors.toList());

            throw new ServiceException("物料编号：{}，类型为：{} ，合成还需要用到如下物料类型：{}，不需要如下物料类型：{}",materialNo,materialType.getName(),
                    Arrays.toString(requiredMaterialTypeNameList.toArray()),Arrays.toString(unwantedMaterialTypeNameList.toArray()));
        }

        materialOfInsert.setChildMaterialList(childMaterialList);

        return Boolean.TRUE;
    }

    /**
     * 订阅 物料新增事件
     * 1.如果录入的类型为 半成品 修改 录入物料的物料类型 下的子物料集合的 物料状态 为 已合并
     * 2.判断产品绑定
     * @param event
     */
    @EventListener(value= TopCentralMaterialEvent.class,condition = "#event.getSignEnum().name()=='INSERT'")
    public TopCentralMaterialEvent insertEvent(TopCentralMaterialEvent event){

        TopCentralMaterial source = event.getSource();

        if(checkTopCentralMaterialForInsert(source)){

            List<TopCentralMaterial> childMaterialList = source.getChildMaterialList();

            //需要将  当前新增物料 的 子物料对象  修改为合并状态
            List<TopCentralMaterial> combinedMaterialListOfUpdate = new ArrayList<>();

            childMaterialList.forEach(material -> {
                TopCentralMaterial combinedMaterialOfUpdate = new TopCentralMaterial();
                combinedMaterialOfUpdate.setId(material.getId());
                combinedMaterialOfUpdate.setMaterialStatus(MaterialStatusEnum.COMBINED_OF_STATUS.getValue());
                combinedMaterialListOfUpdate.add(combinedMaterialOfUpdate);
            });

            materialService.updateBatchById(combinedMaterialListOfUpdate);

            return new TopCentralMaterialEvent(combinedMaterialListOfUpdate, SignEnum.UPDATE,true);
        }
        return null;
    }

    /**
     *
     * @param event
     */
    @EventListener(value= TopCentralMaterialEvent.class,condition ="#event.getSignEnum().name()=='UPDATE'")
    public void updateEvent(TopCentralMaterialEvent event){

        Collection<TopCentralMaterial> materials = event.getSources();

        List<String> materialIdListOfDisable = materials.stream().filter(material -> StringUtils.isNotNull(material.getState()) && material.getState() == State.DISABLE)
                .map(TopCentralMaterial::getId).collect(Collectors.toList());

        if(StringUtils.isNotEmpty(materialIdListOfDisable)){
            List<TopCentralMaterial> materialList = materialService.selectUsableByIds(materialIdListOfDisable, TopCentralMaterial::getId, TopCentralMaterial::getMaterialStatus);
            //不能删除的物料集合
            List<TopCentralMaterial> canNotUpdateMaterials = materialList.stream().filter(material -> !EnumUtils.equals(MaterialStatusEnum.FREE_OF_STATUS, material.getMaterialStatus()))
                    .collect(Collectors.toList());
            if(StringUtils.isNotEmpty(canNotUpdateMaterials)){
                List<String> canNotUpdateMaterialIds = canNotUpdateMaterials.stream().map(TopCentralMaterial::getId).collect(Collectors.toList());
                throw new UpdateDataException.CanNotUpdateStateToDisableException(StringUtils.format("不能将编号为：{} ， 物料状态为：{}、{} 的物料数据 状态修改为 {} ！",
                        Arrays.toString(canNotUpdateMaterialIds.toArray()),
                        Arrays.toString(MaterialStatusEnum.getUnmodifiableSet().stream().map(MaterialStatusEnum::getDescription).toArray()),
                        State.DISABLE.getInfo()));
            }
        }
    }

    /**
     * 删除物料事件  订阅
     * 功能：检测物料是否能进行删除
     * @param event
     */
    @EventListener(value= TopCentralMaterialEvent.class,condition ="#event.getSignEnum().name()=='DELETE'")
    public void deleteEvent(TopCentralMaterialEvent event){

        List<String> materialIds = new ArrayList<>();

        if(event.isContainer()){
            materialIds = CollectionUtils.mapToList(event.getSources(),TopCentralMaterial::getId);
        }else{
            materialIds.add(event.getSource().getId());
        }

        List<TopCentralMaterial> materials = materialService.selectBatchIds(materialIds);
        //不能删除的物料集合
        List<TopCentralMaterial> canNotOperationMaterials = materials.stream().filter(material -> !EnumUtils.equals(MaterialStatusEnum.FREE_OF_STATUS, material.getMaterialStatus()))
                .collect(Collectors.toList());

        if(StringUtils.isNotEmpty(canNotOperationMaterials)){
            //不能删除的物料集合
            List<String> canNotDeleteMaterialIds = CollectionUtils.mapToList(canNotOperationMaterials,TopCentralMaterial::getId);

            throw new ServiceException("不能删除编号为：{} ， 物料状态为：{}、{} 的物料数据！",canNotDeleteMaterialIds,
                    Arrays.toString(MaterialStatusEnum.getUnmodifiableSet().stream().map(MaterialStatusEnum::getDescription).toArray()));
        }

        //k 产品批次号 根据产品批次号分组
        Map<String, List<TopCentralMaterial>> materialListMapGroupByBatchNo = materials.stream().collect(Collectors.groupingBy(TopCentralMaterial::getBatchNo, Collectors.toList()));

        Set<String> batchNoSet = materialListMapGroupByBatchNo.keySet();

        LambdaQueryWrapper<TopCentralMaterial> queryWrapper = materialService.customLambdaQuery();
        queryWrapper.select(TopCentralMaterial::getId,TopCentralMaterial::getBatchNo);
        if(batchNoSet.size()==1){
            queryWrapper.eq(TopCentralMaterial::getId,CollectionUtils.getFirst(batchNoSet));
        }else{
            queryWrapper.in(TopCentralMaterial::getId,batchNoSet);
        }
        Map<String, List<TopCentralMaterial>> materialListMapGroupByBatchNoOfAll = materialService.getBaseMapper().selectList(queryWrapper).stream().collect(Collectors.groupingBy(TopCentralMaterial::getBatchNo, Collectors.toList()));

        List<String> batchNoOfDelete = new ArrayList<>();

        materialListMapGroupByBatchNo.forEach((batchNo, materialsOfGroup) -> {
            List<TopCentralMaterial> materialsAllOfGroup = materialListMapGroupByBatchNoOfAll.get(batchNo);
            if(materialsAllOfGroup.size()-materialsOfGroup.size()==0){
                batchNoOfDelete.add(batchNo);
            }
        });
        if(StringUtils.isNotEmpty(batchNoOfDelete)){
            productService.getBaseMapper().delete(productService.lambdaQuery().in(TopCentralProduct::getBatchNo,batchNoOfDelete));
        }

    }

}
